/************************************************************************************************************************************************
 *   Copyright (C) 2008 by Denjs                                                                                                                *
 *   dplsoft@pisem.net                                                                                                                          *
 *   http://qdroid.berlios.de                                                                                                                   *
 * -----------------------------------------------------------------------                                                                      *
 *   This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or (at your option) any later version.                                      *
 *   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.                                 *
 *   You should have received a copy of the GNU General Public License along with this program; if not, write to the                            *
 *   Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.                                                  *
 * -------------------------------------------------------------------------------------------------------------------------------------------- *
 *     Эта программа является свободным программным обеспечением; вы можете распространять её и/или модифицировать в соответствии с лицензией   *
 *   GNU General Public License  (УНИВЕРСАЛЬНАЯ ОБЩЕСТВЕННАЯ ЛИЦЕНЗИЯ GNU) версии 2.0 или более поздней - на ваш выбор.                         *
 *     Эта программа распространяется  в надежде что она будет вам полезна, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, подразумеваемых или явных; без обещаний *
 *   полезности или применимости в конкретных условиях. Вы используете  данное ПО на свой страх и риск и сами полностью несете                  *
 *   ответственность за последствия. См. GNU General Public License (УНИВЕРСАЛЬНАЯ ОБЩЕСТВЕННАЯ ЛИЦЕНЗИЯ GNU).                                  *
 *     Вы должны получить копию GNU General Public License (УНИВЕРСАЛЬНАЯ ОБЩЕСТВЕННАЯ ЛИЦЕНЗИЯ GNU)с данным ПО; Если этого не случилось,       *
 *   обратитесь в Фонд Свободного Программного Обеспечения :                                                                                    *
 *   Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.                                                  *
 * -------------------------------------------------------------------------------------------------------------------------------------------  *
 * Замечания для территории России: в соответствии с ГК РФ часть 4, начиная использовать данную программу (исполняемые файлы или исходные       *
 * тексты) вы автоматически соглашаетесь с данной лицензией.
 ************************************************************************************************************************************************/

#include "t_QDroid.h"

/* ==================================================================
*/
t_QDroid::t_QDroid(QObject *parent)
    : QObject(parent)
{
 	DebugLevel=0;
 	//	
//	DroidGui	= new t_DroidGui(NULL);
//	ObjectReg	= new t_ObjectRegistry(this);//репозитарий объектов (внешних поставщиков объектов)/ будет описан позже всего.
	//FunctionReg	= new t_FunctionRegistry(this);//репозитарий подключенных функций
	
	QSEngine	= new QScriptEngine();
	FunctionRegItem = new t_FunctionRegistryItem();//новый создадим
	BaseBot		= new t_BaseBot(FunctionRegItem, this); // "первый слуга", базовый бот доступный в скриптовой среде.
		
					// выполняет общие функции типа "кто тут", списко операций и т.п.
					// предположительно - интерфейс для общения с HiveQueen - по загрузке и выгрузке элементов...
					// и управления системой из скриптов.
	QDLibManager = new t_QDLibManager(QSEngine, this); 	// менеджер расширений qDROID

	//2008.12.25 - процесс сборки системы перенесен в init()
}

int t_QDroid::script_list_item_activated ( const QModelIndex & index ) //не гут наверное это здесь вешать но пока ладно. 2008.12.05
{
/*
	//qDebug()<<"t_QDroid::script_list_item_activated ()::[0080] "<<index.row();
	connect_to_regitem(index.row());
    ScriptDescriptionObject->setText("Name:\n    "+ FunctionRegItem->Name+" \nDescription:\n    "+FunctionRegItem->Description+"\nSource File:\n    "+ FunctionRegItem->FileName+"");
*/
}
int t_QDroid::script_list_item_clicked( const QModelIndex & index ) //не гут наверное это здесь вешать но пока ладно. 2008.12.05
{
}

int t_QDroid::script_list_item_doubleClicked( const QModelIndex & index ) //не гут наверное это здесь вешать но пока ладно. 2008.12.05
{
}

/* ==================================================================
*/
t_QDroid::~t_QDroid()
{

}

/* ==================================================================
 инициация и запуск системы. Отсюда все начинается.
*/
int t_QDroid::init(QStringList argList)
{

	BaseBot->useGui = this->useGui;
	BaseBot->platformVersion = this->platformVersion;	
	QString ScriptRegFile;
	//базового бота - сразу засунуть в среду скрипта
	QScriptValue ScriptObjectValue1 = QSEngine->newQObject(BaseBot);
	QSEngine->globalObject().setProperty("BBot", ScriptObjectValue1);
	QScriptValue ScriptObjectValue2 = QSEngine->newQObject(&stdReader);
	QSEngine->globalObject().setProperty("StdStreamsGate", ScriptObjectValue2);
	//:: [[@stdstreamsgate]] [[:qdroid_stdobject]]
	//::====StdStreamsGate====
	//::Объект класса [[stdinreader]]. \n Предоставляет доступ к потокам ввода/вывода для обмена данными с внешней(родительской) средой.
	
	connect(QSEngine,SIGNAL(signalHandlerException(const QScriptValue &)),this,SLOT(signalHandlerException_rcv( const QScriptValue &)));

	//переопределим print() на std_out для BaseBot
	//определим ряд дополнительных функций доступных в QTScript
	define__qdroid_qtscript_functions(QSEngine,BaseBot,QDLibManager);


	//привяжем StdOut от BaseBot к "ЦНС" HiveQueen
	connect(BaseBot,SIGNAL(std_out(QString)),this,SLOT(std_out_resend(QString)));

	//подключим базового бота к нам
	//connect(BaseBot,SIGNAL(send_to_std_err(QByteArray)),this,SLOT(rcv_to_std_out(QByteArray)) );
	//connect(BaseBot,SIGNAL(send_to_std_out(QByteArray)),this,SLOT(rcv_to_std_out(QByteArray)) );
	//connect(this,SIGNAL(send_from_std_in(QByteArray)),BaseBot,SLOT(rcv_from_std_in(QByteArray)) );
	   // и сигналы тоже надо не забыть
	//connect(this,SIGNAL(sig_stdInStream_Closed()),BaseBot,SLOT(slot_stdInStream_Closed()) );


	//подключим нас к иссточнику данных о потоках ввода/вывода
	//connect(this,SIGNAL(send_to_std_err(QByteArray)),&stdReader,SLOT(send_StdErr(QByteArray)) );
	//connect(this,SIGNAL(send_to_std_out(QByteArray)),&stdReader,SLOT(send_StdOut(QByteArray)) );
	//connect(&stdReader,SIGNAL(received_StdIn(QByteArray)),this,SLOT(rcv_from_std_in(QByteArray)) );
	   //и сигналы тоже надо не забыть
	//connect(&stdReader,SIGNAL(stdInStream_Closed()),this,SLOT(slot_stdInStream_Closed()));

	////подтянем управляющие сигналы для std-read-std-write
	//connect(BaseBot,SIGNAL(send_openStdReader()),this,SLOT(rcv_openStdReader()) );// -> запустить обработку потоков ввода
	//connect(BaseBot,SIGNAL(send_closeStdReader()),this,SLOT(rcv_closeStdReader()) );// -> запустить обработку потоков ввода
	//// -> остановить обработку потоков ввода-вывода



//--------------
	//получим имя файла для целей фильтрации параметров из-командной строки
	QString thisPathName=QCoreApplication::applicationDirPath();
	QString thisFileName=(QCoreApplication::applicationFilePath()).remove(0,thisPathName.size()+1);
	//if (DebugLevel>=4)
		 //qDebug() << "thisFileName="<<thisFileName;
		
	int startPos=0;
	if ((argList.at(0).endsWith(thisFileName)) //на тот случай если запускаемся по относитльному пути
	  or (argList.at(0).endsWith("qdroid")) // да. даже так. пока именно так. глупо, да. Под linux это стандартная ссылка на наш файл.
//	  or (argList.at(0)=="qdroid") // да. даже так. пока именно так. глупо, да.
	   )
		startPos++;//если словили cвое имя файла - то его надо пропустить
		/*
		весь косяк в том, что когда мы под windows стартуем как дочерний процесс
		 - в некоторых случаях нам могут не дать наше имя в качестве 
		нулевого параметра... из-за этого этот кусок кода и появился.
		На самом деле надо проверить, возможно QCoreApplication::arguments() 
		и исправляет данную ситацию, но пока будем думать что нет.
		по крайней мере чтение аргументов из *argv[] в main() приводит 
		именно к обнаружению такой ситуации.
		*/
	/*
	   разбор argList
	*/
//--------------
	startPos=1;//НО пока требуем чтобы в начале всегода шло имя файла, а аргументы начинались со второго по порядку

	QString currentParam;
	QString scriptFileName;
	QStringList scriptArgList;
	int state=0;//state of option recognition machine
	int activeParam=0;//количество активных/полезных параметров 
	qDebug() <<" arg list size::"<<argList.size();
	for (int q=startPos;q<argList.size();q++)
	  {
		currentParam=argList.at(q);
		qDebug() <<" arg::"<<q<<" = ["<< currentParam <<"]";

		if ((state==0) 
		  and ((currentParam==QString("-c"))
		      or (currentParam==QString("--config"))
		      )
		   ) // not emplemented yet: use cpecial config file
		  {
			qDebug() << "WARNING:: {-c|--config} config file option does not supported yet";
			state=1;//waiting for config file name
			continue;
		  };
		
		if (state==1)//waiting for config file name
		  {
			//qDebug() << "WARNING:: {-c|--config} config file option does not supported yet";
			state=0;//waiting for config file name
			//configFileName=currentParam;
			continue;
		  };
		
		
		if (
			(state==0)
			and 
			(
			  (currentParam==QString("--help"))
			  or (currentParam==QString("-h"))
			  or (currentParam==QString("--usage"))
			)
		   )
		  {
			std_out_resend_HTML( QString("\nUsage help:\n qdroid [-h|--help|--usage] \n qdroid [{-c|--config} configFileName] scriptFileName.qs [scriptOptions] "));
			QApplication::quit();
			return -1;
		  };
		
		if (state==10) //идет набор параметров скрипту
		  {
			scriptArgList.append(currentParam);
			continue;
		  };
		if (state==0) 
		  {
			state=10;// waiting for arguments for list
			scriptFileName=currentParam;//Если состояние нулевое - то думаем что имя скрипта для исполнения.
			activeParam++;
			continue;
		  };
		
	  };
	this->BaseBot->arguments=scriptArgList;//not good! not signalslot!
	if (activeParam<1)
	  {
		std_out_resend_HTML( QString("\n:-0138 ERR No Script File Given. Type \"qdroid --usage\" to get usage help."));
		QApplication::quit();
		return -1;
	  };
	
	//if (argList.size()>1) //пока просто так. Если что-то указали в командной строке - значит будет режим "shell"
		WorkProfile=QDroid_WorkProfile_SHELL;
	//else
	//	WorkProfile=QDroid_WorkProfile_GUI;
	/* TODO: добавить сюда работу со встроенным документом shell
		ввод данных через StdIn ???? */
	
	if (DebugLevel>=4) qDebug()<<"t_QDroid::init:[00110]::Switching to mode ["<< WorkProfile <<"]";
	
	/**/
	// shell-вариант - вывод на поле DroidGui
	if (WorkProfile==QDroid_WorkProfile_SHELL)
	  {
		// надо загрузить единственный скрипт и выполнить его.
		// где лежит скрипт - из самого аргумента - он нам передается с полным путем.
		if (DebugLevel>=4) qDebug()<<"t_QDroid::init:[00192] <CP reached>";
		//TMP: загрузим скрипт
		//bool ThisIsStandAloneScriptFile=true;
		//FunctionReg->load_script_list(argList.at(1),ThisIsStandAloneScriptFile);
		//FunctionRegItem=NULL;
		//connect_to_regitem(0);//инициирует указатель FunctionRegItem на заданный элемент списка
			//FunctionRegItem=FunctionReg->get_reg_item(RegItemIndex);
				//t_FunctionRegistryItem *TmpElement;
				//FunctionRegItem = new t_FunctionRegistryItem();//новый создадим
				FunctionRegItem->FileName=scriptFileName;//FileName=argList.at(1);
				FunctionRegItem->Name="NoName";	// REM!
				FunctionRegItem->ID="NoID";	// REM!
				FunctionRegItem->Description="loaded from file";// REM!
				
			connect(FunctionRegItem,SIGNAL(std_out(QString)),this,SLOT(std_out_resend(QString)));
		
				//привяжем FunctionRegItem к HiveQueen - которая собственно и будет выполнять скрипты
			// temporary added 2008.11.19
			connect(FunctionRegItem,SIGNAL(script_to_be_executed(QStringList)),this,SLOT(script_to_execute(QStringList)));
			//return 0;
			if (FunctionRegItem->load_from_file("")!=0)//empty file name - it means to use FunctionRegItem->FileName
			  {
				        std_out_resend_HTML( QString("\n:-0227 ERR Error loading Script File Given. Cheсk if file is exist."));
					QApplication::quit();
					return -2;
			  };
	  };
	
	//qDebug()<<"Current path is ["<<QDir::currentPath()<<"]";
	//qDebug()<<"Application path is ["<<QCoreApplication::applicationDirPath()<<"]";


	//***************************************************************************
	//ВСЕ! с этого момента ввод/вывод из sdtin|sdtout подностью отдается в скрипт.
	stdReader.open();
	//stdReader.run(); но запуск чтения - не ранее чем по команде из скрипта
	
	if (DebugLevel>=5) qDebug() << "QDroid::init()[00211]: exit. QDroid starting.";
	// shell-вариант - запустимся)
	if (WorkProfile==QDroid_WorkProfile_SHELL)
	  {
		FunctionRegItem->init_execution();//TO REWRITE!
		//  и вызвать непосредственно script_to_execute() самого себя
	  };

	//std_out_resend("[LOG] t_QDroid::init()::[0058] ");
 
	if (DebugLevel>=5) qDebug() << "QDroid::init()[00211]: exit. QDroid finished.";

	// TODO if (!QSEngine->isEvaluating()) QApplication::quit();// в люблм случае выкидывает.
	//	даже если объекты созданы BUG!

	return 0;
}

/* ==================================================================
  тестовый слот.
*/
//int t_QDroid::init2()
//{
//	//DroidGui->setWindowTitle("init2 resived");
//	return 0;
//}


/* ==================================================================
 слот для сбора stdout-данных от подчиненных.
 пока просто пересылаем далее.

*/
void t_QDroid::std_out_resend(QString Message)
{
//	if (WorkProfile==QDroid_WorkProfile_SHELL)
	  qDebug()<< Message;
//	 else
//	  {
//	 	emit std_out(Message);
//	  };
}

void t_QDroid::std_out_resend_HTML(QString Message)
{
//	if (WorkProfile==QDroid_WorkProfile_SHELL)
	  qDebug()<< Message;
//	 else
//	  {
//	 	emit std_out_HTML(Message);
//	  };
}

/* ==================================================================
	тут ловим скрипт который нас просят исполнить.
 	и собственно отправляем его на обработку.
*/
void t_QDroid::script_to_execute(QStringList _ScriptSource)
{
	QString DBGPlace="t_QDroid::script_to_execute()";
	//int DebugLevel=2;
	//if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00259]::[CheckPoint]";
	
	//emit std_out("[ ] t_QDroid::script_to_execute()::[0099]: Signal Resived");
	QString ScriptInStr=QString();
	QString Message,StrPart,color,TmpStr;
	QStringList StrList;
	if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00259]::[CheckPoint]";
	StrList=_ScriptSource;
	StrList=prerpocess_script(StrList);//сразу пропустим его через препроцессор.
	
	if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00262]::[CheckPoint]";
	
	int q;
	for (;!StrList.isEmpty();)
		{
			TmpStr=StrList.takeFirst();
			ScriptInStr.append(TmpStr).append(" ");
		};
	//qDebug() << "Starting to evaluate script" <<"================\n"<< ScriptInStr <<"================ \n";
	if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00261]::[CheckPoint]";
	std_out_resend("Starting to execute script.\n=={START}==================================");

/*TMP TOD */	
	QScriptValue res=QSEngine->evaluate(ScriptInStr);
	//QScriptValue res;
	if (QSEngine->hasUncaughtException())
	  {
	     int line = QSEngine->uncaughtExceptionLineNumber();
	     std_out_resend( QString("=={ERROR}=================================="));
	     std_out_resend_HTML( QString("Uncaught exception at line %1 : ").arg(line).append(res.toString())+"");
	     Message="";StrList=QSEngine->uncaughtExceptionBacktrace();
	     for (;!StrList.isEmpty();) Message.append(StrList.takeFirst()).append(" ");
	     std_out_resend( QString("Exception BackTrace: ")+ Message +"");

	     Message=QString();StrList=_ScriptSource;
	     int old=-1;
	     for (q=1;!StrList.isEmpty();q++)
	    	  {	/*
				 StrPart=StrList.takeFirst();
				 if ((q>line-5)&(q<line+5))
				  {
					 if (q!=line) color="green"; else color="red";
					 if (old!=q)
						 Message.append(QString("%1:<font color="+color+">").arg(q)+StrPart+"</font><br>\n");
					 else
						 Message.append(QString("  :")+StrPart+"<br>");
				  };
				 old=q;
				 if (StrPart.endsWith("\\\n")) {q--;};
			*/  // 2009.09.04 изменено под работу только в консоли
				 StrPart=StrList.takeFirst();
				 if ((q>line-5)&(q<line+5))
				  {
					 if (q==line) color="ERR->"; else color="    :";
					 if (old!=q)
						 Message.append(QString("%1:"+color+"").arg(q)+StrPart+"");
					 else
						 Message.append(QString("    :")+StrPart+"");
				  };
				 old=q;
				 if (StrPart.endsWith("\\\n")) {q--;};//<--------- не помню с чем связано, кажется с многострочными строками.заканчиващимися на \n 
					// т.е. для скрипового ядра - это одна строка, 
					// а для файла - несколько...

	    	  };
	     std_out_resend_HTML( QString("Source: \n")+ Message );
	     std_out_resend_HTML( QString("\n:-0323 ERR Uncaught exception at line %1 : ").arg(line).append(res.toString())+"");

	  }
	else
	 std_out_resend( QString("=={FIN}=====================================\nCommon result is: ").append(res.toString()) );
}

//==================================================================
/* SLOT
*/
void t_QDroid::signalHandlerException_rcv ( const QScriptValue & exception )
{
	if (QSEngine->hasUncaughtException())
	  {
	    int line = QSEngine->uncaughtExceptionLineNumber();
	    QString Message1 = QString("=={ERROR}==================================");
	    QString Message2 = QString("\n:-0338 ERR Uncaught exception at line %1 : ").arg(line).append(exception.toString());
		std_out_resend(Message1);//std_out_resend for a some time we needs to see  it in debug or
		std_out_resend(Message2);
	  };
}
//===================================================================
/*
	реализация препроцессора. пока тут, потом
        если функционал будет расширен - надо будет вынести в отдельный класс
*/
QStringList t_QDroid::prerpocess_script(QStringList _ScriptSource)
{

	QString DBGPlace="t_QDroid::prerpocess_script";

    //if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00259]::[CheckPoint]";
	if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00328]::[CheckPoint].Started.";

	int q;
	QStringList StrList=_ScriptSource;
	QStringList OutStrList;
	QString TmpStr;
	QChar PreProPrefix='#';//префикс препроцессорной инструкции
	if (DebugLevel>=4) qDebug()<<"DBG.L4::"<<DBGPlace<<":[00334]::[CheckPoint]";

	for (;!StrList.isEmpty();)
	  {
		TmpStr=StrList.takeFirst();
		if (DebugLevel>=4) qDebug()<<"DBG.L5::"<<DBGPlace<<":[00334]::[CheckPoint]";

		if (TmpStr.trimmed().startsWith(PreProPrefix))
		 {  //значит это препроцессорная инструкция.. её надо обработать...
			if (DebugLevel>=5) qDebug()<<"DBG.L5::"<<DBGPlace<<":[00355]:: strdump:"<<TmpStr;
			OutStrList.append("//"+TmpStr);//пока просто "закомментим"
			//никаких include "file.iqs" и др. инструкций
		 }
		else
		 {
			if (DebugLevel>=5) qDebug()<<"DBG.L5::"<<DBGPlace<<":[00350]:: strdump:"<<TmpStr;
			OutStrList.append(TmpStr);
		 };
	  };

   return OutStrList;
}

/*/
void rsv_openStdReader();// -> запустить обработку потоков ввода
void rsv_closeStdReader();// -> остановить обработку потоков ввода-вывода

//---------------------------------------------------------------------
//это поучатели - два от внутренних объъектов системы, один от "ядра"
void t_QDroid::rcv_to_std_err(QByteArray Message) //--> подключен к сигналам внутренних объектов
{
 emit send_to_std_err(Message);//перешлем его
}

//---------------------------------------------------------------------
void t_QDroid::rcv_to_std_out(QByteArray Message) //--> подключен к сигналам внутренних объектов
{
 emit send_to_std_out(Message);//перешлем его
}

//---------------------------------------------------------------------
void t_QDroid::rcv_from_std_in(QByteArray Message)// -> к нему подключен SIGNAL QDroid.rsv_from_std_in
{
 if (DebugLevel>=4) 
 	qDebug()<<"CP:t_QDroid::rcv_from_std_in()[0471]";
 emit send_from_std_in(Message);//перешлем его
}

//---------------------------------------------------------------------
void t_QDroid::rcv_openStdReader() // -> запустить обработку потоков ввода
{
 //stdReader.run();
   stdReader.start();
}

//---------------------------------------------------------------------
void t_QDroid::rcv_closeStdReader()// -> остановить обработку потоков ввода-вывода
{
 stdReader.reset();
}



//---------------------------------------------------------------------
void t_QDroid::slot_stdInStream_Closed()
{
 emit sig_stdInStream_Closed();
}

/**/
